<!DOCTYPE html>
<html lang="cn">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- <script src="./plane.js"></script> -->
    <title>飞机大战</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        canvas {
            position: absolute;
            top: 0;
            left: 0;
        }
        
        body {
            width: 100vw;
            height: 100vh;
        }
    </style>
    </style>
</head>

<body>
    <audio src="https://webfs.hw.kugou.com/202312131420/1e8f640849ef748768e19499779b74d9/v2/178f890105e2305438245ad23fd96ffe/G232/M0A/0A/04/yJQEAF9RI-uAb0OhAB8GCj15-JQ512.mp3"></audio>
    <canvas id="canvas"></canvas>
    <script>
        // 获取画布比例9：16
        // 先创建素材

        const bgImage = new Image();
        bgImage.src = "https://upload-images.jianshu.io/upload_images/14016888-07ab84dec27c6612.png?imageMogr2/auto-orient/strip|imageView2/2/w/480/format/webp";
        const userplane = new Image();
        userplane.src = "https://upload-images.jianshu.io/upload_images/14016888-e3d2f21c6e2cc265.png?imageMogr2/auto-orient/strip|imageView2/2/w/46/format/webp";
        const enemyplane = new Image();
        enemyplane.src = "https://upload-images.jianshu.io/upload_images/14016888-6b9624c1deb561ae.png?imageMogr2/auto-orient/strip|imageView2/2/w/69/format/webp";
        const bullet1 = new Image();
        bullet1.src = "https://upload-images.jianshu.io/upload_images/14016888-ff389915bee91bc6.png?imageMogr2/auto-orient/strip|imageView2/2/w/5/format/webp";
        const bullet2 = new Image();
        bullet2.src = "https://upload-images.jianshu.io/upload_images/14016888-8fe683430d5cd233.png?imageMogr2/auto-orient/strip|imageView2/2/w/5/format/webp";
        const enemydown1 = new Image();
        enemydown1.src = "https://upload-images.jianshu.io/upload_images/14016888-5de26da9ac6b826d.png?imageMogr2/auto-orient/strip|imageView2/2/w/69/format/webp";
        const enemydown2 = new Image();
        enemydown2.src = "https://upload-images.jianshu.io/upload_images/14016888-36c5ade8705d1b12.png?imageMogr2/auto-orient/strip|imageView2/2/w/69/format/webp";
        const enemydown3 = new Image();
        enemydown3.src = "https://upload-images.jianshu.io/upload_images/14016888-70f509fa1fcb2c06.png?imageMogr2/auto-orient/strip|imageView2/2/w/69/format/webp";
        const enemydown4 = new Image();
        enemydown4.src = "https://upload-images.jianshu.io/upload_images/14016888-ca62513fe0687c25.png?imageMogr2/auto-orient/strip|imageView2/2/w/69/format/webp";
        const zd = new Image();
        zd.src = "https://upload-images.jianshu.io/upload_images/14016888-a75c7fdeac527b6b.png?imageMogr2/auto-orient/strip|imageView2/2/w/60/format/webp";
        const zdnum = new Image();
        zdnum.src = "https://upload-images.jianshu.io/upload_images/14016888-78f4b7ce15c74a8c.png?imageMogr2/auto-orient/strip|imageView2/2/w/63/format/webp";
        /**
         * 判断是否碰撞
         * 因为只记录的飞机的x,y坐标所有要加上size,才是飞机的面积
         */

        class Game {
            constructor() {
                this.Images = {
                    0: enemyplane,
                    1: enemydown1,
                    2: enemydown2,
                    3: enemydown3,
                    4: enemydown4,
                    zd: zd,
                    zdnum: zdnum
                }
                this.size = 50; // 格子大小
                this.bulletSize = 10; // 子弹大小
                this.requestAFId = null;
                this.isGameOver = false; // 是否开始游戏
                this.isGameOver = false;
                this.cvs = document.getElementById("canvas"),
                    this.ctx = document.getElementById("canvas").getContext("2d"),
                    this.config = {
                        width: window.innerWidth,
                        height: window.innerHeight,
                        offsetLeft: this.cvs.offsetLeft,
                        offsetTop: this.cvs.offsetTop,
                    };

                this.planeSpeed = 2; // 飞机移动速度
                this.userBulletSpeed = 10; // 玩家子弹移动速度
                this.enemyBulletSpeed = 3; // 敌人子弹移动速度
                this.userBulletStep = 200; // 玩家子弹间隔时间
                this.enemyBulletStep = 1500; // 敌人子弹间隔时间
                this.bullets = {
                    user: [],
                    enemy: []
                }; // 子弹数组 enemy弃用
                this.lifeNum = 3;
                this.life = this.lifeNum;
                this.userPointer = {
                    x: this.size * 4,
                    y: document.body.clientHeight / 1.5,
                    width: this.size,
                    height: this.size
                }; // 玩家飞机坐标
                this.enemyPointer = [{
                        x: this.size * 4,
                        y: 0,
                        is: false,
                        width: this.size,
                        height: this.size,
                        destroyStep: 0,
                        image: this.Images[0],
                        bullet: []
                    }] // 敌人飞机坐标
                this.enemyPlaneNum = 50; // 场上敌人飞机数量
                this.enemyPlaneStep = 500; // 生成敌人飞机间隔
                this.touchEnter = false; // 是否点击到玩家飞机
                this.collision = false; // 是否碰撞
                this.score = 0; // 分数
                this.enemyDestroyTime = 200; // 敌人飞机销毁时间

                this.enemyBulletSetI = null; // 敌人子弹定时器
                this.userBulletSetI = null; // 玩家子弹定时器
                this.enemyCreateSetI = null; // 生成敌机定时器

                // 炸弹
                this.zdSpeed = 5;
                this.zdStep = 1;
                this.zdnum = 0;
                this.zd = [];

                // min/max
                this.min = 0;
                this.max = document.body.clientWidth / this.size;
                console.dir(document.body);
                this.startBg();
                this.addEvent();
            }
            gameStart() {
                if (!this.isActive) {
                    this.isActive = true;
                    this.init();
                }
            }
            startBg() {
                    const cvs = this.cvs;
                    const ctx = this.ctx;
                    this.BG(cvs, ctx)
                    ctx.font = '50px 宋体';
                    ctx.fillText("点击开始", cvs.width / 2 - 100, cvs.height / 2);
                }
                //重新开始
            restart() {
                this.clearEnemyCreateSetI();
                this.isActive = false;
                this.isGameOver = false;
                this.life = this.lifeNum;
                this.zdStep = 1;
                this.zdnum = 0;
                this.zd = [];
                clearInterval(this.enemyBulletSetI);
                clearInterval(this.userBulletSetI);
                clearInterval(this.enemyCreateSetI);
                this.score = 0;
                this.enemyPointer = [];
                this.userPointer = {
                    x: this.size * 4,
                    y: document.body.clientHeight / 1.5,
                    width: this.size,
                    height: this.size
                }; // 玩家飞机坐标
            }
            restartBg() {
                this.restart();
                const cvs = this.cvs;
                const ctx = this.ctx;
                cvs.removeEventListener("mousedown", this.mousedownFn);
                this.mouseupFn();
                ctx.clearRect(0, 0, cvs.width, cvs.height);
                this.BG(cvs, ctx);
                ctx.font = '50px 宋体';
                console.log(this.userPointer)
                ctx.fillText("点击重新开始", cvs.width / 2 - 100 - 50, cvs.height / 2);
                this.addEvent();
            }

            // 清除敌人飞机定时器
            clearEnemyCreateSetI() {
                    for (let i = 0; i < this.enemyPointer.length; i++) {
                        const item = this.enemyPointer[i];
                        clearInterval(item.setInterval);
                    }
                }
                // 奖励
            reward() {}
            init() {
                    const {
                        width,
                        height
                    } = this.config;
                    const cvs = this.cvs;
                    cvs.width = width;
                    cvs.height = height;

                    this.render();
                    this.enemyCreate();
                    this.enemyBullet();
                    this.userBullet();
                    this.enemyBulletSetI = setInterval(() => {
                        this.enemyBullet();
                    }, this.enemyBulletStep);
                    this.userBulletSetI = setInterval(() => {
                        this.userBullet();
                    }, this.userBulletStep);
                }
                // 生成炸弹道具
            zdCreate() {
                    const step = this.score / this.zdStep;
                    if (step == 100) {
                        ++this.zdStep;
                        const obj = {
                            x: this.getRandom(this.min, this.max) * this.size,
                            y: 0,
                            width: this.size,
                            height: this.size
                        }
                        this.zd.push(obj);
                    }
                }
                // 判断是否触碰到炸弹道具
            touchzd() {
                this.zdMove();
                for (let i = 0; i < this.zd.length; i++) {
                    const item = this.zd[i];
                    if (this.isCollisionFn(item, this.userPointer)) {
                        this.zd.splice(i, 1);
                        this.zdnum++;
                    }
                }
            }
            zdMove() {
                    for (let i = 0; i < this.zd.length; i++) {
                        const item = this.zd[i];
                        if (item.y < this.cvs.height) {
                            item.y += this.zdSpeed;
                        } else {
                            this.zd.splice(i, 1);
                        }
                    }
                }
                // 绘制炸弹
            drawzd() {
                    this.touchzd();
                    for (let i = 0; i < this.zd.length; i++) {
                        const item = this.zd[i];
                        this.ctx.drawImage(this.Images.zd, item.x, item.y, this.size, this.size);
                    }
                }
                // 绘制炸弹数量
            drawzdnum() {
                    this.ctx.drawImage(this.Images.zdnum, this.cvs.width - this.size * 2, 20, this.size, this.size);
                    this.ctx.font = "30px 宋体";
                    this.ctx.fillStyle = "yellow";
                    this.ctx.fillText(this.zdnum, this.cvs.width - this.size + 10, this.size);
                }
                // 使用炸弹
            usezd() {
                    if (this.zdnum <= 0) return;
                    this.zdnum--;
                    this.usezdAll()
                }
                // 使用炸弹请出屏幕所有敌人
            usezdAll() {
                    this.isBulletCollision(true);
                }
                // 获取随机数
            getRandom(min, max) {
                return Math.random() * (max - min + 1) + min;
            }
            drawScore() {
                    this.ctx.font = "20px 宋体";
                    this.ctx.fillStyle = "yellow";
                    this.ctx.fillText(this.score + '分', 10, 20);
                }
                // 背景
            BG(cvs, ctx) {
                    const {
                        width,
                        height
                    } = this.config;
                    ctx.clearRect(0, 0, width, height);
                    cvs.width = width;
                    cvs.height = height;
                    ctx.drawImage(bgImage, 0, 0, width, height);
                }
                // 生成玩家发射子弹
            userBullet() {
                    const {
                        x,
                        y
                    } = this.userPointer;
                    this.bullets.user.push({
                        x: x + this.size / 2 - 3.5,
                        y: y - 25,
                        width: this.bulletSize,
                        height: this.bulletSize * 2
                    });
                }
                // 生成敌人子弹
            enemyBullet() {
                    if (this.enemyPointer.length <= 0) return;
                    for (let i = 0; i < this.enemyPointer.length; i++) {
                        if (this.enemyPointer[i].is) continue;
                        const {
                            x,
                            y
                        } = this.enemyPointer[i];
                        const item = this.enemyPointer[i];
                        item.bullet.push({
                            bulletX: x + this.size / 2 - 3.5,
                            bulletY: y + 25,
                            bulletWidth: this.bulletSize,
                            bulletHeight: this.bulletSize * 2
                        })
                    }
                }
                // 绘制所有子弹
            bulletDraw(ctx) {
                    for (let i = 0; i < this.bullets.user.length; i++) {
                        const {
                            x,
                            y,
                            width,
                            height
                        } = this.bullets.user[i];
                        ctx.drawImage(bullet1, x, y, width, height);
                    }
                    for (let i = 0; i < this.enemyPointer.length; i++) {
                        const item = this.enemyPointer[i];
                        for (let j = 0; j < item.bullet.length; j++) {
                            const {
                                bulletX: x,
                                bulletY: y,
                                bulletWidth: width,
                                bulletHeight: height
                            } = item.bullet[j];
                            ctx.drawImage(bullet2, x, y, width, height);
                        }
                    }
                }
                // 子弹移动
            bulletMove() {
                    for (let i = 0; i < this.bullets.user.length; i++) {
                        const {
                            y
                        } = this.bullets.user[i];
                        this.bullets.user[i].y -= this.userBulletSpeed;
                        if (y <= 0) {
                            this.bullets.user.splice(i, 1);
                            i--;
                        }
                    }
                    for (let i = 0; i < this.enemyPointer.length; i++) {
                        const item = this.enemyPointer[i];
                        if (item.is == true && item.bullet.length == 0 && !item.setInterval) {
                            this.enemyPointer.splice(i, 1);
                            continue;
                        }
                        for (let j = 0; j < item.bullet.length; j++) {
                            const itemJ = item.bullet[j];
                            itemJ.bulletY += this.enemyBulletSpeed;
                            if (itemJ.bulletY >= this.config.height) {
                                item.bullet.splice(i, 1);
                            }
                        }
                    }
                    this.bulletHit();
                }
                // 子弹抵消
            bulletHit() {
                    this.enemyPointer.map(itemp => {
                        itemp.bullet.map((itemb, indexb) => {
                            this.bullets.user.map((itemu, indexu) => {
                                if (this.isCollisionFn(itemu, {
                                        x: itemb.bulletX,
                                        y: itemb.bulletY,
                                        width: itemb.bulletWidth,
                                        height: itemb.bulletHeight
                                    })) {
                                    itemp.bullet.splice(indexb, 1);
                                    this.bullets.user.splice(indexu, 1);
                                }
                            })
                        })
                    })
                }
                // 生成敌人飞机
            enemyCreate() {
                this.enemyCreateSetI = setInterval(() => {
                    this.enemyCreateFn();
                }, this.enemyPlaneStep);
            }
            enemyCreateFn() {
                    if (this.enemyPointer.length >= this.enemyPlaneNum) return;
                    const x = this.getRandom(this.min, this.max);
                    this.enemyPointer.push({
                        x: x * this.size,
                        y: 0,
                        is: false,
                        width: this.size,
                        height: this.size,
                        destroyStep: 0,
                        image: this.Images[0],
                        bullet: []
                    });
                }
                // 绘制敌对飞机
            enemy(ctx) {
                    for (let i = 0; i < this.enemyPointer.length; i++) {
                        const item = this.enemyPointer[i];
                        if (item.is && item.destroyStep >= 4) {
                            continue;
                        };
                        const {
                            x,
                            y,
                            image
                        } = this.enemyPointer[i];
                        ctx.drawImage(image, x, y, this.size, this.size);
                    }
                }
                // 绘制被击毁的动画
            enemyDestroy() {
                for (let i = 0; i < this.enemyPointer.length; i++) {
                    const item = this.enemyPointer[i];
                    if (!item.is) continue;
                    if (item.destroyStep >= 4) {
                        clearInterval(item.setInterval);
                        item.setInterval = null;
                        continue;
                    };
                    item.destroyStep++;
                    item.image = this.Images[item.destroyStep];
                }
            }

            // 判断是否碰撞
            isCollision() {
                    for (let i = 0; i < this.enemyPointer.length; i++) {
                        const item = this.enemyPointer[i];
                        if (item.is) continue;
                        if (this.isCollisionFn(this.userPointer, item)) {
                            --this.life;
                            this.enemyPointer.splice(i, 1);
                            if (this.life == 0) {
                                this.isGameOver = true;
                            }
                        }
                    }
                }
                // 判断矩形是否相交
            isCollisionFn(rectangle1, rectangle2) {
                    const x1 = rectangle1.x;
                    const y1 = rectangle1.y;
                    const width1 = rectangle1.width;
                    const height1 = rectangle1.height;

                    const x2 = rectangle2.x;
                    const y2 = rectangle2.y;
                    const width2 = rectangle2.width;
                    const height2 = rectangle2.height;

                    const left1 = x1;
                    const right1 = x1 + width1;
                    const top1 = y1;
                    const bottom1 = y1 + height1;

                    const left2 = x2;
                    const right2 = x2 + width2;
                    const top2 = y2;
                    const bottom2 = y2 + height2;

                    return !(right1 < left2 || left1 > right2 || bottom1 < top2 || top1 > bottom2);
                }
                // 判断子弹是否击中敌人或被击中
                // is:清空敌人
            isBulletCollision(is = false) {
                    for (let i = 0; i < this.enemyPointer.length; i++) {
                        const item = this.enemyPointer[i];
                        if (is) {
                            this.score += 10;
                            item.is = true;
                            item.destroyStep = 1;
                            item.image = this.Images[item.destroyStep];
                            item.bullet = [];
                            if (item.setInterval) return;
                            item.setInterval = setInterval(() => {
                                this.enemyDestroy();
                            }, this.enemyDestroyTime);
                            continue;
                        }
                        if (item.is) continue;
                        if (this.isBulletCollisionFn(item)) {
                            this.score += 10;
                            item.is = true;
                            item.destroyStep = 1;
                            item.image = this.Images[item.destroyStep];

                            if (item.setInterval) return;
                            item.setInterval = setInterval(() => {
                                this.enemyDestroy();
                            }, this.enemyDestroyTime);
                        }
                    }
                    for (let i = 0; i < this.enemyPointer.length; i++) {
                        const item = this.enemyPointer[i];
                        for (let j = 0; j < item.bullet.length; j++) {
                            const itemJ = item.bullet[j];
                            const obj = {
                                x: itemJ.bulletX,
                                y: itemJ.bulletY,
                                width: itemJ.bulletWidth,
                                height: itemJ.bulletHeight
                            }
                            if (this.isCollisionFn(this.userPointer, obj)) {
                                --this.life;
                                item.bullet.splice(j, 1)
                                if (this.life == 0) {
                                    this.isGameOver = true;
                                }
                            }
                        }
                    }
                }
                // 是否击中敌人
            isBulletCollisionFn(plane) {
                    for (let i = 0; i < this.bullets.user.length; i++) {
                        if (this.isCollisionFn(this.bullets.user[i], {
                                x: plane.x,
                                y: plane.y,
                                width: plane.width,
                                height: plane.height
                            })) {
                            return true;
                        }
                    }
                }
                // 生成玩家
            player(ctx) {
                    const {
                        x,
                        y
                    } = this.userPointer;
                    ctx.beginPath();
                    ctx.drawImage(userplane, x, y, this.size, this.size);
                    ctx.rect(x, y, this.size, this.size);
                    ctx.stroke()
                    ctx.closePath();
                    this.drawLife();
                }
                // 绘制生命条
            drawLife() {
                    const {
                        x,
                        y
                    } = this.userPointer;
                    const ctx = this.ctx;
                    const step = this.lifeNum / this.life;
                    ctx.beginPath();
                    ctx.rect(x, y - 10, this.size, 10);
                    ctx.stroke();
                    ctx.closePath();
                    ctx.beginPath();
                    ctx.rect(x + 1, y - 9, this.size / step, 8)
                    ctx.fillStyle = "red";
                    ctx.fill();
                    ctx.closePath();
                }
                // 敌对飞机移动
            enemyMove() {
                    for (let i = 0; i < this.enemyPointer.length; i++) {
                        const item = this.enemyPointer[i];
                        if (item.is) continue;

                        item.y += this.planeSpeed;
                        if (item.y >= this.size * 16) {

                            this.enemyPointer.splice(i, 1);
                        }
                    }
                    this.isBulletCollision();
                    this.isCollision();
                }
                // 渲染
            render() {
                    const cvs = this.cvs;
                    const ctx = this.ctx;
                    this.BG(cvs, ctx);
                    this.player(ctx);
                    this.enemy(ctx);
                    this.bulletMove();
                    this.bulletDraw(ctx);
                    this.enemyMove();
                    this.zdCreate();
                    this.drawzd();
                    this.drawScore();
                    this.drawzdnum();
                    if (this.isGameOver) {
                        cancelAnimationFrame(this.requestAFId);
                        alert("游戏结束");
                        this.restartBg();
                    } else {
                        this.requestAFId = requestAnimationFrame(() => {
                            this.render();
                        })
                    }
                }
                // 判断是否点击到飞机
            isTouchPlane = (init) => {
                const {
                    x: ux,
                    y: uy
                } = this.userPointer;
                const {
                    x,
                    y
                } = init;
                if (x >= ux && x <= ux + this.size && y >= uy && y <= uy + this.size) {
                    this.touchEnter = true;
                } else {
                    this.touchEnter = false;
                }
            };
            // 添加事件
            mousedownFn = (event) => {
                // openFullscreen(document.documentElement);
                if (!this.isActive) {
                    this.gameStart();
                    return;
                }
                const {
                    x,
                    y
                } = this.eventValue(event);
                const {
                    offsetLeft: left,
                    offsetTop: top
                } = this.cvs;
                const e = {
                    x: x - left,
                    y: y - top
                };
                this.isTouchPlane(e);
                if (this.touchEnter) {
                    document.addEventListener(emove, this.mousemoveFn, false);
                    document.addEventListener(eup, this.mouseupFn);
                }
            };
            mousemoveFn = (event) => {
                if (!this.touchEnter) return;
                const {
                    x: offsetX,
                    y: offsetY
                } = this.eventValue(event);
                const {
                    offsetLeft: left,
                    offsetTop: top
                } = this.cvs;
                const x = offsetX - left - this.size / 2;
                const y = offsetY - top - this.size / 2;
                this.userPointer.x = x;
                this.userPointer.y = y
            };
            mouseupFn = () => {
                document.removeEventListener(emove, this.mousemoveFn);
                document.removeEventListener(eup, this.mouseupFn);
            };
            // 判断pc还是移动端使用不同的值
            eventValue(e) {
                    let x, y;
                    if (e.touches) {
                        x = e.touches[0].clientX;
                        y = e.touches[0].clientY;
                    } else {
                        x = e.clientX;
                        y = e.clientY;
                    }
                    return {
                        x,
                        y
                    }
                }
                // 空格按键
            keydownFn = (e) => {
                if (e.keyCode == 32) {
                    this.usezd();
                }
            };
            addEvent = () => {
                const canvas = this.cvs;
                document.addEventListener('keydown', this.keydownFn)
                canvas.addEventListener(edown, this.mousedownFn);
            };
        }
        let game;

        function isMobileDevice() {
            return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
        }

        function openFullscreen(elem) {
            if (elem.requestFullscreen) {
                elem.requestFullscreen();
            } else if (elem.mozRequestFullScreen) { /* Firefox */
                elem.mozRequestFullScreen();
            } else if (elem.webkitRequestFullscreen) { /* Chrome, Safari 和 Opera */
                elem.webkitRequestFullscreen();
            } else if (elem.msRequestFullscreen) { /* IE/Edge */
                elem.msRequestFullscreen();
            }
        }
        let edown, emove, eup;
        if (isMobileDevice()) {
            edown = "touchstart";
            emove = "touchmove";
            eup = "touchend";
        } else {
            edown = "mousedown";
            emove = "mousemove";
            eup = "mouseup";
        }

        function start() {
            if (game) {
                game.re();
            }
            game = new Game();
        }
        window.onload = () => {
            game = new Game();
        };
    </script>
</body>

</html>